import { useCallback, useState } from "react";
import { isEqual } from "lodash";

// 用于描述响应信息对象的结构
interface ResponseInfo {
  params: unknown[]; // 类型为未知数组，存储请求参数
  lastRequestTime: number; // 数值类型，记录上次请求的时间戳
  getData: () => Promise<unknown>; // 函数类型，返回Promise对象，用于异步获取数据
}

// 提取 Promise 的解析值类型
type PromiseValue<T> = T extends Promise<infer U> ? U : T;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type CommonFunc = (...p: any[]) => any;

type ResponseData<T> = {
  data: T;
  isSuccess: boolean;
  isError: boolean;
};

type ResponseValue<F extends CommonFunc> = {
  apiCall: F;
  data: ResponseData<PromiseValue<ReturnType<F>>>;
  loading: boolean;
};

const responseWeakMap = new WeakMap<CommonFunc, ResponseInfo>();

const genUseSingletonApi =
  <T extends CommonFunc>(apiCall: T) =>
  (instance: CommonFunc, expiration: number) =>
  () => {
    type APIReturnData = PromiseValue<ReturnType<typeof apiCall>>;
    const [data, setData] = useState<APIReturnData>();
    const [loading, setLoading] = useState<boolean>();
    const apiCallFunc = useCallback(async (...params: Parameters<T>) => {
      setLoading(true);
      const endCallback = (res: APIReturnData) => {
        setLoading(false);
        setData(res);
      };
      const requestInfo = responseWeakMap.get(instance);
      if (requestInfo) {
        const {
          getData: getResponseInfo,
          params: oldParams,
          lastRequestTime,
        } = requestInfo;
        const hasResponse =
          isEqual(oldParams, params) &&
          +new Date() - lastRequestTime <= expiration;
        if (hasResponse) {
          const res = await getResponseInfo();
          endCallback(res as APIReturnData);
          return res;
        }
      }
      let resolve: (value: APIReturnData) => void = () => {};
      const promise: Promise<APIReturnData> = new Promise((resolveCb) => {
        resolve = resolveCb;
      });
      responseWeakMap.set(instance, {
        getData: () => promise,
        params,
        lastRequestTime: +new Date(),
      });
      const result = await apiCall(...params);
      resolve(result);
      endCallback(result);
      return result;
    }, []);

    return {
      apiCall: apiCallFunc,
      data,
      loading,
    };
  };

const someAPISingletonInstance = genUseSingletonApi(fetchDataFunc);

export const useAccountingList = someAPISingletonInstance(
  someAPISingletonInstance,
  1000
);
